<?php
/**
 * @author		João Batista Neto
 * @brief		Implementação da API REST do BuscaPé
 * @package		dso.buscape
 */

require_once 'rpo/base/Object.php';
require_once 'rpo/http/HTTPConnection.php';
require_once 'rpo/http/HTTPRequest.php';
require_once 'rpo/http/HTTPBody.php';
require_once 'rpo/http/HTTPHeaderField.php';
require_once 'dso/buscape/result/BuscapeXMLResultDirector.php';

/**
 * @brief		API BuscaPé
 * @details		<p>
 * A API (Interface de Programação de Aplicativos) do BuscaPé é um conjunto de funções e padrões estabelecidos para criação
 * de aplicativos customizados, em que há necessidade de uso da base de dados de produtos, ofertas e serviços oferecidos pelo
 * BuscaPé.</p>
 * <p>
 * Para integrar à API do BuscaPé é necessário primeiramente
 * <a href="http://developer.buscape.com/blog/tutoriais/como-obter-um-id-para-sua-aplicacao/" target="_blank">obter um ID</a>
 * para sua aplicação, que deverá ser usado em todas as requisições feitas ao serviço que for solicitado. É através deste ID
 * que é feita a medição do rendimento da sua aplicação.
 * </p>
 * <p>
 * Após obter o ID para sua aplicação, a classe AbstractBuscapeAPI deverá ser derivada, implementando o método getApplicationId()
 * conforme pode ser visto nesse exemplo: @ref BuscapeAPIImp.php
 * </p>
 * @class		AbstractBuscapeAPI
 * @ingroup		API
 * @example		BuscapeAPIImp.php
 */
abstract class AbstractBuscapeAPI extends Object {
	/**
	 * @brief	ID da aplicação
	 * @var		string
	 */
	private $applicationId;

	/**
	 * @brief	Objeto de conexão HTTP
	 * @var		HTTPConnection
	 */
	private $connection;

	/**
	 * @brief	Formato da requisiçãp
	 * @var		string
	 */
	private $format = 'xml';

	/**
	 * @brief	Constroi o objeto da API BuscaPé
	 * @param	$connection HTTPConnection
	 * @param	$format string
	 * @throws	InvalidArgumentException Se o formato não for <b>xml</b> ou <b>json</b>.
	 */
	public function __construct( HTTPConnection $connection , $format = 'xml' ){
		if ( $format == 'json' || $format == 'xml' ){
			$this->applicationId = $this->getApplicationId();
			$this->connection = $connection;
			$this->format = $format;
		} else {
			throw new InvalidArgumentException( sprintf( 'Formato precisa ser xml ou json, %s foi dado.' , $format ) );
		}
	}

	/**
	 * @brief	Recupera o ID da aplicação
	 * @return	string
	 */
	abstract protected function getApplicationId();

	/**
	 * @brief	Executa a requisição à API
	 * @param	$service string O nome do serviço que será requisitado
	 * @param	$query array Parâmetros da requisição
	 * @return	BuscapeResult
	 */
	protected function execute( $service , array $query ){
		$body = new HTTPBody();

		foreach ( $query as $key => $value ){
			$body[ $key ] = $value;
		}

		$request = new HTTPRequest( HTTPRequestMethod::GET );
		$request->setBody( $body );
		$request->setURI( sprintf( 'http://%s/service/%s/%s/%s/' , $this->getHost() , $service , $this->applicationId , $this->getCountryCode() ) );
		$request->addHeader( new HTTPHeaderField( 'Host' , $this->getHost() ) );
		$request->addHeader( new HTTPHeaderField( 'Accept-Encoding' , 'UTF-8' ) );
		$request->addHeader( new HTTPHeaderField( 'User-Agent' , 'PHP-Rest/0.1' ) );

		if ( $this->format == 'xml' ){
			$request->addHeader( new HTTPHeaderField( 'Accept' , 'application/xml' ) );
			$director = new BuscapeXMLResultDirector();
		} else {
			throw new RuntimeException( 'Não implementado' );
		}

		return $director->setHTTPResponse( $this->connection->open()->request( $request ) )->getResult();
	}

	/**
	 * @brief	Busca uma categoria pelo ID
	 * @details O serviço de procura de categorias permite que sejam exibidas informações relativas às categorias.
	 * <p>
	 * É possível obter categorias, produtos ou ofertas informando apenas um ID de categoria.<br /><br />
	 * <b>Existem três tipos de categorias:</b>
	 * @li	<b>Categoria Raiz</b>	- É o primeiro nível da árvore. Através dela obtemos
	 * as subcategorias. (BuscapeComposite)
	 * @li	<b>Subcategoria</b>	- É o segundo nível da árvore, em que podemos obter
	 * outras subcategorias ou categorias finais. (BuscapeComposite)
	 * @li	<b>Categoria Final</b>	- É o último nível da árvore, estando relacionado a
	 * uma lista de produtos, ofertas ou serviços. (BuscapeLeaf).
	 * <br /><br />
	 * Veja @ref CategoryListPage para maiores informações.</p><br /><br />
	 * @param	$id integer O ID da categoria
	 * @return	BuscapeResult
	 */
	public function findCategoryByID( $id ){
		return $this->execute( 'findCategoryList' , array( 'categoryId' => $id ) );
	}

	/**
	 * @brief	Busca uma categoria por uma palavra chave
	 * @details O serviço de procura de categorias permite que sejam exibidas informações relativas às categorias.
	 * <p>
	 * É possível obter categorias, produtos ou ofertas informando apenas um ID de categoria.<br /><br />
	 * <b>Existem três tipos de categorias:</b>
	 * @li	<b>Categoria Raiz</b>	- É o primeiro nível da árvore. Através dela obtemos
	 * as subcategorias. (BuscapeComposite)
	 * @li	<b>Subcategoria</b>	- É o segundo nível da árvore, em que podemos obter
	 * outras subcategorias ou categorias finais. (BuscapeComposite)
	 * @li	<b>Categoria Final</b>	- É o último nível da árvore, estando relacionado a
	 * uma lista de produtos, ofertas ou serviços. (BuscapeLeaf).
	 * <br /><br />
	 * Veja @ref CategoryListPage para maiores informações.</p><br /><br />
	 * @param	$keyword string Palavra chave que identifica a categoria
	 * @return	BuscapeResult
	 */
	public function findCategoryByKeyword( $keyword ){
		return $this->execute( 'findCategoryList' , array( 'keyword' => $keyword ) );
	}

	/**
	 * @brief	Recupera uma lista de ofertas para um produto
	 * @param	$productId integer O ID do produto
	 * @return	BuscapeResult
	 */
	public function findOfferList( $productId ){
		return $this->execute( 'findOfferList' , array( 'productId' => $productId ) );
	}

	/**
	 * @brief	Recupera uma lista de produtos
	 * @details	A lista de produtos pode ser feita utilizando o ID da categoria e opcionalmente
	 * utilizando uma palavra-chave do produto
	 * @param	$categoryId integer O ID da categoria
	 * @param	$keyword string Palavra chave
	 * @return	BuscapeResult
	 */
	public function findProductListByCategoryId( $categoryId , $keyword = null ){
		$params = array( 'categoryId' => $categoryId );

		if ( !is_null( $keyword ) ){
			$params[ 'keyword' ] = $keyword;
		}

		return $this->execute( 'findProductList' , $params );
	}

	/**
	 * @brief	Recupera uma lista de produtos utilizando uma palavra chave
	 * @param	$keyword string Palavra chave
	 * @return	BuscapeResult
	 */
	public function findProductListByKeyword( $keyword ){
		return $this->execute( 'findProductList' , array( 'keyword' => $keyword ) );
	}

	/**
	 * @brief	Recupera o código do país
	 * @return	string
	 */
	public function getCountryCode(){
		return 'BR';
	}

	/**
	 * @brief	Recupera a URL do serviço
	 * @return	string
	 * @see		AbstractBuscapeAPI.getServer()
	 */
	public function getHost(){
		return sprintf( '%s.buscape.com' , $this->getServer() );
	}

	/**
	 * @brief	Recupera o servidor onde será feita a consulta
	 * @return	string
	 */
	public function getServer(){
		return 'sandbox';
	}

	public function topProducts(){
		return $this->execute( 'topProducts' , array() );
	}

	public function viewProductDetails( $productId ){
		return $this->execute( 'viewProductDetails' , array( 'productId' => $productId ) );
	}

	/**
	 * @brief	Recupera detalhes do vendedor
	 * @param	$sellerId integer ID do vendedor
	 * @return	BuscapeSeller
	 */
	public function viewSellerDetails( $sellerId ){
		return $this->execute( 'viewSellerDetails' , array( 'sellerId' => $sellerId ) )->getSeller();
	}

	/**
	 * @brief	Recupera a avaliação dos usuários para um produto
	 * @param	$productId integer O ID do produto
	 * @return	BuscapeResult
	 */
	public function viewUserRatings( $productId ){
		return $this->execute( 'viewUserRatings' , array( 'productId' => $productId ) );
	}
}